package com.ldn.basic.utils.tencent;

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;

/**
 * @author 李大牛家的牛 (scafel@163.com) 
 * @createDate 2024/8/14 - 22:07
 * @description ...
 **/
@Slf4j
public class WechatPayUtil {

    /** 商户号 */
    public static String merchantId;

    /** 商户API私钥路径 */
    public static String privateKeyPath;

    /** 商户证书序列号 */
    public static String merchantSerialNumber;

    /** 商户APIV3密钥 */
    public static String apiV3Key;

    public static JsapiServiceExtension service;
    public static Config config;

    public static boolean isInit = false;


    public static void initPayMerchant(String merchatId, String privateKeyPath, String merchantSerialNumber, String apiV3Key) {
        merchantId = merchatId;
        privateKeyPath = privateKeyPath;
        merchantSerialNumber = merchantSerialNumber;
        apiV3Key = apiV3Key;
        // 初始化商户配置
        config = new RSAAutoCertificateConfig.Builder()
                        .merchantId(merchantId)
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        // 初始化服务
        service = new JsapiServiceExtension.Builder()
                        .config(config)
                        .signType("RSA") // 不填默认为RSA
                        .build();
        isInit = true;
    }

    /** 关闭订单 */
    public static void closeOrder() {

        CloseOrderRequest request = new CloseOrderRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        service.closeOrder(request);
    }

    /** JSAPI支付下单，并返回JSAPI调起支付数据 */
    public static PrepayWithRequestPaymentResponse createPrepay(WechatPayRequestDto requestDto)  throws RuntimeException {
        if (!isInit){
            throw new RuntimeException("请先初始化商户配置");
        }
        try {
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(requestDto.getTotal());
            amount.setCurrency(requestDto.getCurrency());
            request.setAmount(amount);
            request.setAppid(requestDto.getAppid());
            request.setAttach(requestDto.getAttach());
            request.setDescription(requestDto.getDescription());
            request.setMchid(requestDto.getMchid());
            request.setNotifyUrl(requestDto.getNotify_url());
            request.setOutTradeNo(requestDto.getOut_trade_no());
            request.setTimeExpire(requestDto.getTime_expire());
            Payer payer = new Payer();
            payer.setOpenid(requestDto.getOpenid());
            request.setPayer(payer);
            // 调用接口
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
            log.info("支付jsapi调用e => {}", response);
            return response;
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            throw new RuntimeException(e.getHttpRequest().toString());
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            throw new RuntimeException(e.getResponseBody());
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            throw new RuntimeException(e.getMessage());
        }
    }

    /** 微信支付订单号查询订单 */
    public static Transaction queryOrderById(String mchid, String transactionId) {
        if (!isInit){
            throw new RuntimeException("请先初始化商户配置");
        }
        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        request.setMchid(mchid);
        request.setTransactionId(transactionId);
        // 调用接口
        return service.queryOrderById(request);
    }

    /** 商户订单号查询订单 */
    public static Transaction queryOrderByOutTradeNo(String mchid, String outTradeNo) {
        if (!isInit){
            throw new RuntimeException("请先初始化商户配置");
        }
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        request.setMchid(mchid);
        request.setOutTradeNo(outTradeNo);
        // 调用接口
        return service.queryOrderByOutTradeNo(request);
    }

    /** 解析微信支付回调通知 **/
    public static Transaction payNotificationParser(RequestParam requestParam) throws RuntimeException {
        if (!isInit){
            throw new RuntimeException("请先初始化商户配置");
        }
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig) config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            return parser.parse(requestParam, Transaction.class);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed { }", e);
            throw new RuntimeException("微信支付验证失败");
        } catch (Exception ex){
            log.error("微信支付验证失败 { }", ex);
            throw new RuntimeException("微信支付验证失败");
        }
    }

    /** 微信订单退款 **/
    public static Refund orderRefund(WechatPayRefundDto requestDto) throws RuntimeException{
        if (!isInit){
            throw new RuntimeException("请先初始化商户配置");
        }
        CreateRequest request = new CreateRequest();
        AmountReq amount = new AmountReq();
        amount.setTotal(requestDto.getTotal());
        amount.setCurrency(requestDto.getCurrency());
        amount.setRefund(requestDto.getRefund());
        request.setAmount(amount);
        request.setNotifyUrl(requestDto.getNotify_url());
        request.setOutRefundNo(requestDto.getOut_refund_no());
        request.setOutTradeNo(requestDto.getOut_trade_no());
        request.setReason(requestDto.getReason());
        try {
            // 初始化服务
            RefundService refundService = new RefundService.Builder().config(config).build();
            return refundService.create(request);
        } catch (HttpException ex) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控，更多方法见HttpException定义
            log.error("微信支付退款失败 HttpException { }", ex);
            throw new RuntimeException("微信支付退款失败"+ex.getMessage());
        } catch (ServiceException ex) { // 服务返回状态小于200或大于等于300，例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
            log.error("微信支付退款失败 ServiceException { }", ex);
            throw new RuntimeException("微信支付退款失败"+ex.getErrorMessage());
        } catch (MalformedMessageException ex) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
            log.error("微信支付退款失败 MalformedMessageException { }", ex);
            throw new RuntimeException("微信支付退款失败"+ex.getMessage());
        } catch (Exception ex) {
            log.error("微信支付退款失败 Exception { }", ex);
            throw new RuntimeException("微信支付退款失败"+ex.getMessage());
        }

    }

}
